#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <FreeImage.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h> 


#define GLUT_WHEEL_UP 3
#define GLUT_WHEEL_DOWN 4

int win_width = 1920,win_height = 1080;

struct _AUX_RGBImageRec
{
	unsigned long sizeX;
	unsigned long sizeY;
	unsigned char *data;
};

typedef struct _AUX_RGBImageRec AUX_RGBImageRec;

GLuint texture[1];												//存储一个纹理
GLfloat rtri;													//存储旋转变量，不断改变器值
GLfloat zoom = 1.0f;											//缩放程度，默认位1，用于放大或缩小
GLfloat xpos = 0.f;												//x方向移动
GLfloat ypos = 0.f;												//y方向移动

void init();
void display(void);
void reshape(int w, int h);
void keyboard(unsigned char key, int x, int y);
void processMouse(int button, int state, int x, int y);
void changeParam();
int LoadGLTextures();
GLboolean LoadBmp(const char *filename, AUX_RGBImageRec* texture_image);

GLuint tex2D;
GLfloat angle;



/*初始化openGL*/
void init()
{

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);	//黑色背景
	glShadeModel(GL_SMOOTH);				//启用阴影平滑

	LoadGLTextures();		//载入纹理
	glEnable(GL_TEXTURE_2D);				//启动纹路映射
	glClearDepth(1.0f);						//设置深度缓存
	glClearDepth(GL_DEPTH_TEST);			//启用深度测试
	glDepthFunc(GL_LEQUAL);					//所作深度测试的类型
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	//真正精细的透视修正
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);


	glPushMatrix();
	//glTranslatef(0.0f, 0.0f, -5.0f);
	glRotated(1.0f, ypos, xpos, 0);

	glEnable(GL_TEXTURE_2D);

	/** 选择纹理 */
	glBindTexture(GL_TEXTURE_2D, texture[0]);

	/** 开始绘制四边形 */
	glBegin(GL_QUADS);
	
	/// 前侧面
	glNormal3f(0.0f, 0.0f, 1.0f);                               /**指定法线指向观察者 */
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);

	/// 后侧面
	glNormal3f(0.0f, 0.0f, -1.0f);                              /** 指定法线背向观察者 */
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f);

	/// 顶面
	glNormal3f(0.0f, 1.0f, 0.0f);                               /**指定法线向上 */
	glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, 1.0f, 1.0f);
	glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, 1.0f, -1.0f);

	/// 底面
	glNormal3f(0.0f, -1.0f, 0.0f);                              /** 指定法线朝下 */
	glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 0.0f);glVertex3f(1.0f, -1.0f, 1.0f);
	glTexCoord2f(1.0f, 1.0f);glVertex3f(1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f, -1.0f, -1.0f);

	/// 右侧面
	glNormal3f(1.0f, 0.0f, 0.0f);                               /**指定法线朝右 */
	glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);

	/// 左侧面
	glNormal3f(-1.0f, 0.0f, 0.0f);                              /**指定法线朝左 */
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
	
	glEnd();
	
	glDisable(GL_TEXTURE_2D);
	
	glFlush();
	glutPostRedisplay();

	// 执行绘图命令
	//glFlush();
	return ;

	// // 清除之前帧数据
	glClear(GL_COLOR_BUFFER_BIT);

	// 绘制三角形
	glBegin(GL_TRIANGLE_FAN);
	glColor3f(1, 0, 0);
	glVertex3f(-1, -1, -5);
	glColor3f(0, 1, 0);
	glVertex3f(1, -1, -5);
	glColor3f(0, 0, 1);
	glVertex3f(0, 1, -5);
	glColor3f(1, 0, 1);
	glVertex3f(0.0f+xpos, 0.0f+ypos, 0.0f+zoom);
	glEnd();

	// 执行绘图命令
	glFlush();
	return ;
	
	glClear(GL_COLOR_BUFFER_BIT);

	glColor3f(1.0, 0, 0);
	glutWireTeapot(1);
	//glRotatef(0.5, 0.0f, 1.0f, 0.0f);

	glFinish();
	return ;

	glClear(GL_COLOR_BUFFER_BIT);	//清楚平面和深度缓存
	glLoadIdentity();				//重置当前的模型观察矩阵

	//加上xpos，ypos产生移动效果
	glTranslatef(0.0f+xpos, 0.0f+ypos, -5.0f);	//移入屏幕5个单位
	//此处进行缩放， x,y,z,方向均按此比例缩放
	glScalef(zoom, zoom, zoom);
	
	glRotatef(rtri, 0.0f, 1.0f, 0.0f);			//绕Y轴旋转
	//glBindTexture(GL_TEXTURE_2D, texture[0]);
	//glBegin(GL_QUADS);							//绘制四边形
	GLUquadric* quadricObj = gluNewQuadric();	//gluNewQuadric创建一个新的二次曲面对象
	gluQuadricTexture(quadricObj, GL_TRUE);
	
	/*参数1：二次曲面对象指针
	**参数2：球半径
	**参数3：Z轴方向片数，经度方向
	**参数4：Y周方向片数，纬度方向
	*/
	gluSphere(quadricObj, 1, 100, 100);
	//gluQuadricDrawStyle(quadricObj,400);

	//gluDeleteQuadric删除一个二次曲面对象
	gluDeleteQuadric(quadricObj);
	glEnd();

	//通知硬件绘制图像
	glFinish();
	return ;
}

















































































































int main(int argc, char **argv)
{

	glutInit(&argc, argv);	//GLUT环境初始化
	
	//显示模式初始化
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	
	//定义窗口daxiao
	glutInitWindowSize(win_width, win_height);
	
	//定义窗口位置
	glutInitWindowPosition(0,0);
	
	//显示窗口位置，窗口标题为执行函数名
	glutCreateWindow("CYY_Demo");

	//调用OpenGL初始化函数
	init();
	
	//注册OpenGL绘图函数
	glutDisplayFunc(display);
	
	//注册窗口大小改变时的响应函数
	glutReshapeFunc(reshape);
	
	//注册键盘响应事件
	glutKeyboardFunc(keyboard);
	
	//注册鼠标响应事件
	glutMouseFunc(processMouse);
	
	////注册自动旋转的函数
	glutIdleFunc(changeParam);
	
	//进入GLUT消息循环，开始执行函数
	glutMainLoop();
	return 0;
}





/*载入位图（调用上面的代码）转换成纹理*/
int LoadGLTextures()
{
	int Status = FALSE;		//状态指示器
	AUX_RGBImageRec *textureImage;	//创建纹理的存储空间
	textureImage = malloc(sizeof(AUX_RGBImageRec));

	//载入位图，检查有无错误，如果位图没找到则退出
	if (LoadBmp("earth.bmp", textureImage))
	{
		Status = TRUE;
		glGenTextures(1, &texture[0]);	//创建纹理的存储空间
		glBindTexture(GL_TEXTURE_2D, texture[0]);/*使用来自位图数据生成的典型纹理*/

		/*生成纹理*/
		glTexImage2D(GL_TEXTURE_2D, 0, 3, textureImage->sizeX, textureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, textureImage->data);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	}

	//纹理是否存在
	if (textureImage)
	{
		//纹理图像是否存在
		if (textureImage->data)
		{
			free(textureImage->data);	//释放纹理图像占用的内存
		}
		free(textureImage);	//释放图像结构
	}

	return Status;
}


/*窗口大小发生变化时调用*/
void reshape(int w, int h)
{
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	//利用glOrtho创建一个正交平行的视景体
	if (w <= h)
	{

		glOrtho(-1.5, 1.5, -1.5*(GLfloat)h / (GLfloat)w, 
				1.5 *(GLfloat)h / (GLfloat)w, -10.0, 10.0);
	}
	else
	{

		glOrtho(-1.5*(GLfloat)w / (GLfloat)h, 1.5*(GLfloat)w / (GLfloat)h,
				-1.5, 1.5, -10.0, 10.0);
	}

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();	
}

/*定义对键盘的响应函数*/
void keyboard(unsigned char key, int x, int y)
{
	switch ((key))
	{
		/*ESC键退出*/
		case 27:
			exit(0);
			break;
		case '+':	//放大
			zoom += 0.03;
			break;
		case '-':	//缩小
			zoom -= 0.03;
			break;	 
		case 'w':	//上移
			ypos += 0.03;
			break; 
		case 's':	//下移
			ypos -= 0.03;
			break; 
		case 'a':	//左移
			xpos -= 0.03;
			break; 
		case 'd':	//右移
			xpos += 0.03;
			break;			  
		default:
			break;
	}

	printf("Enter Key %c zoom = %lf, xpos = %lf, ypos = %lf \n", key, zoom, xpos, ypos);
}

//处理鼠标事件
void processMouse(int button, int state, int x, int y)
{
	if (state == GLUT_UP && button == GLUT_WHEEL_UP)
	{
		//滚轮向上，表示放大，增大缩放比例
		zoom += 0.02;
		printf("Mouse Wheel UP\n");
		glutPostRedisplay();
	}
	if (state == GLUT_UP && button == GLUT_WHEEL_DOWN)
	{
		//滚轮向下，表示缩小，减少缩放比例
		if (zoom > 0.02)
		{
			printf("Mouse Wheel Down\n");
			zoom -= 0.02;
		}

		glutPostRedisplay();
	}
}

// 自动旋转函数,用于修改绘制时所需要的参数
void changeParam()
{
	rtri += 0.5f;
	glutPostRedisplay();
}

/*
*利用freeimage加载bmp图像
*此函数在Linux系统上可以作为util调用
*/
GLboolean LoadBmp(const char *filename, AUX_RGBImageRec* texture_image)
{
	FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType(filename, 0);

	FIBITMAP *dib = FreeImage_Load(fifmt, filename, 0);
	dib = FreeImage_ConvertTo24Bits(dib);

	int width = FreeImage_GetWidth(dib);
	int height = FreeImage_GetHeight(dib);

	BYTE *pixels = (BYTE*) FreeImage_GetBits(dib);
	int pix = 0;

	if (texture_image == NULL)
	{
		return FALSE;
	}

	texture_image->data = (BYTE*) malloc(width * height * 3);
	texture_image->sizeX = width;
	texture_image->sizeY = height;

	for (pix = 0; pix < width * height; pix++)
	{
		texture_image->data[pix*3 + 0] = pixels[pix * 3 + 2];
		texture_image->data[pix*3 + 1] = pixels[pix * 3 + 1];
		texture_image->data[pix*3 + 2] = pixels[pix * 3 + 0];
	}

	FreeImage_Unload(dib);

	return TRUE;
}

